const _ = require?.('lodash');

import * as arrayUtils from './arrayUtils';

const objUtils: any = require?.('./objUtils'),
  moment: any = require?.('moment');

/**
 * 将对象转换为键值对数组，键名转换为蛇形命名法
 * @param obj - 要转换的对象
 * @returns 键值对数组，每个元素为 [key, value] 格式
 * 
 * 示例：
 * 输入: { testAbc: '123', abCde: '555' }
 * 输出: [ ['test_abc', '123'], ['ab_cde', '555'] ]
 * 
 * 用途：主要用于生成数据库 INSERT 和 UPDATE SQL 语句
 */
export function mypairs(obj: any = {}): any {
  // 如果对象为空或不存在，返回空对象
  if (!obj) {
    return {}
  }

  // 将对象的所有属性名转换为蛇形命名法（snake_case）
  obj = objUtils?.snakeCase?.(obj);

  // 使用 lodash 的 toPairs 方法将对象转换为键值对数组
  let pairs: any = _?.toPairs?.(obj);

  // 如果转换失败，返回空对象
  if (!pairs) {
    return {}
  }

  // 遍历每个键值对，将值转换为字符串或数字格式
  pairs?.forEach?.(item => {
    item[1] = toStrOrNum?.(item?.[1]);
  });

  // 过滤掉值为 null 或 undefined 的键值对
  pairs = pairs?.filter?.(item => {
    return item?.[1] !== null && item?.[1] !== undefined;
  });

  // 如果过滤后没有数据，返回空对象
  if (!pairs) {
    return {}
  }

  return pairs;
}

/**
 * 根据对象生成 WHERE SQL 条件语句
 * @param obj - 包含查询条件的对象
 * @param tableName - 表名（可选），用于生成带表名的字段引用
 * @returns WHERE SQL 条件字符串，以 AND 开头
 * 
 * 示例：
 * 输入: obj = { trueName: 'jack hao', sex: '1' }, tableName = 'staffInfo'
 * 输出: " AND staff_info.true_name = 'jack hao' AND staff_info.sex = '1' "
 */
export function where(obj: any = {}, tableName = ''): string {
  // 如果对象为空，返回空格
  if (!obj) {
    return ' ';
  }

  // 将表名转换为蛇形命名法
  tableName = _?.snakeCase?.(tableName);

  // 将对象的所有属性名转换为蛇形命名法
  obj = objUtils?.snakeCase?.(obj);

  // 获取键值对数组
  const pairs: any = mypairs?.(obj);

  // 如果没有键值对，返回空格
  if (!pairs) {
    return ' ';
  }

  let sql = ' ';

  // 遍历每个键值对，生成 SQL 条件
  pairs?.forEach?.(item => {
    sql = sql + ' AND ';

    const itemElement: any = item?.[1];

    let t = '';

    // 如果指定了表名，添加表名前缀
    if (tableName) {
      t = ' ' + tableName + '.';
    }

    // 如果是数字类型或包含 DATE_format 函数，不加引号
    if (_?.isNumber?.(itemElement) || itemElement?.indexOf?.('DATE_format?.(') > 0) {
      sql = sql + t + _?.head?.(item) + ' = ' + itemElement + ' ';
    } else {
      // 字符串类型加单引号
      sql = sql + t + _?.head?.(item) + " = '" + itemElement + "' ";
    }
  });

  return sql;
}

/**
 * 生成模糊查询的 LIKE SQL 条件
 * @param columns - 要搜索的字段名数组
 * @param searchValues - 搜索关键词，多个关键词用空格分隔
 * @param formName - 表别名，默认为 't'
 * @returns LIKE SQL 条件字符串
 * 
 * 示例：
 * 输入: columns = ['name', 'description'], searchValues = 'apple fruit'
 * 输出: " AND ( (1 < 0) OR ( t.name LIKE '%apple%' ) OR ( t.name LIKE '%fruit%' ) OR ( t.description LIKE '%apple%' ) OR ( t.description LIKE '%fruit%' ) ) "
 */
export function like(
  columns: any[] = [],
  searchValues = '',
  formName = 't'
): string {
  let sql = ' ';

  // 将搜索关键词按空格分割成数组
  const searchValueArr: string[] = _?.split?.(searchValues, ' ');

  // 参数验证：搜索关键词、字段数组和分割后的数组都必须存在
  if (
    !(searchValues) ||
    !(columns) ||
    !(searchValueArr)
  ) {
    return sql;
  }

  // 将表别名转换为蛇形命名法
  formName = _?.snakeCase?.(formName);

  // 开始构建 OR 条件，使用 (1 < 0) 作为初始条件
  sql = sql + ' AND ( (1 < 0) ';

  // 遍历每个字段
  columns?.forEach?.(item => {
    // 将字段名转换为蛇形命名法
    item = _?.snakeCase?.(item);

    // 为每个搜索关键词生成 LIKE 条件
    searchValueArr?.forEach?.(itemSearchValue => {
      sql += ` OR ( ${formName}.${item} LIKE '%${itemSearchValue}%' ) `;
    });
  });

  sql = sql + ' ) ';

  return sql;
}

/**
 * 生成 ORDER BY SQL 排序语句
 * @param sort - 排序规则数组，每个元素包含 sortBy（排序字段）和 descending（是否降序）
 * @param tableName - 表别名，默认为 ' t'
 * @returns ORDER BY SQL 语句
 * 
 * 示例：
 * 输入: sort = [{ sortBy: 'name', descending: false }, { sortBy: 'age', descending: true }]
 * 输出: " ORDER BY  t.name  ASC  ,  t.age  DESC   "
 */
export function order(sort, tableName = ' t',) {

  // 如果没有排序规则，返回空格
  if (!sort || sort?.length < 1) return ' '

  let sql = ' '

  let first = true

  // 遍历每个排序规则
  for (const one of sort) {

    // 将排序字段名转换为蛇形命名法
    const sortBy = _?.snakeCase?.(one?.sortBy);

    // 获取是否降序的标志
    const descending = one?.descending;

    // 生成排序条件
    sql += ` ${tableName}.${sortBy} ${descending ? ' DESC ' : ' ASC '} `;

    // 如果不是第一个排序条件，添加逗号分隔符
    if (!first) sql = ' , ' + sql

    first = false

  }

  return ' ORDER BY ' + sql + ' '

}

/**
 * 生成 LIMIT SQL 分页语句
 * @param startRow - 起始行数（从0开始）
 * @param pageSize - 每页大小
 * @param total - 总记录数
 * @returns LIMIT SQL 语句
 * 
 * 示例：
 * 输入: startRow = 20, pageSize = 20
 * 输出: " LIMIT 20, 20 "
 */
export function limit(startRow = 0, pageSize = 20, total: number,): string {
  // 如果起始行数无效或小于0，设置为0
  if (_?.isNil?.(startRow) || startRow < 0) {
    startRow = 0;
  }

  // 如果每页大小无效或小于1，设置为总记录数（即不分页）
  if (_?.isNil?.(pageSize) || pageSize < 1) {
    pageSize = total;
  }

  return ` LIMIT ${startRow}, ${pageSize} `;
}

/**
 * 生成统计查询的 SQL 语句
 * @param fromSql - FROM 子句
 * @param whereSql - WHERE 条件子句
 * @returns SELECT COUNT(*) 的 SQL 语句
 * 
 * 用途：计算符合条件的数据数量，为分页的 total 字段准备数据
 */
export function selectCount(fromSql = '', whereSql = ''): string {
  return ` SELECT COUNT(*) AS count_0 ${fromSql} WHERE 1>0 ${whereSql} `;
}

/**
 * 生成分页查询的完整 SQL 语句
 * @param selectSql - SELECT 子句
 * @param fromSql - FROM 子句
 * @param whereSql - WHERE 条件子句
 * @param orderBySql - ORDER BY 子句，默认为按 order_num 升序
 * @param limitSql - LIMIT 子句，默认为前20条记录
 * @returns 完整的 SELECT 分页查询 SQL 语句
 */
export function selectPage(
  selectSql = '',
  fromSql = '',
  whereSql = '',
  orderBySql = ' ORDER BY t.order_num ASC ',
  limitSql = ' LIMIT 0, 20 '
): string {
  return (
    selectSql +
    ' ' +
    fromSql +
    ' WHERE 1>0 ' +
    whereSql +
    ' ' +
    orderBySql +
    ' ' +
    limitSql
  );
}

/**
 * 根据页码和每页大小计算起始行数
 * @param pageNum - 页码（从1开始）
 * @param pageSize - 每页大小
 * @returns 起始行数（从0开始）
 * 
 * 示例：
 * 输入: pageNum = 3, pageSize = 20
 * 输出: 40
 */
export function getStart(pageNum = 1, pageSize = 20): number {
  return (pageNum - 1) * pageSize;
}

/**
 * 将各种数据类型转换为适合 SQL 的字符串或数字格式
 * @param obj - 要转换的值
 * @returns 转换后的字符串或数字，无法转换时返回空格
 * 
 * 转换规则：
 * - null/undefined -> 空格
 * - boolean -> '1' 或 '0'
 * - Date -> DATE_format 函数调用
 * - 数字类型 -> 原值
 * - 字符串 -> 原值
 * - 其他类型 -> 空格
 */
export function toStrOrNum(obj = ''): string {
  // 检查是否为 null、undefined 或空值
  if (_?.isNil?.(obj)) {
    return ' '
  }

  // 明确检查 null 和 undefined
  if (_?.isNull?.(obj) || _?.isUndefined?.(obj)) {
    return ' '
  }

  // 布尔值转换为 '1' 或 '0'
  if (_?.isBoolean?.(obj)) {
    if (obj) {
      return '1';
    }

    return '0';
  }

  // 日期类型转换为 DATE_format 函数调用
  if (_?.isDate?.(obj)) {
    return (
      " DATE_format?.('" +
      moment(obj)?.format?.('YYYY-MM-DD HH:mm:ss') +
      "', '%Y-%m-%d %H:%i:%S') "
    );
  }

  // 有限数字或整数直接返回
  if (_?.isFinite?.(obj) || _?.isInteger?.(obj)) {
    return obj;
  }

  // NaN 返回空格
  if (_?.isNaN?.(obj)) {
    return ' '
  }

  // 原生函数返回空格
  if (_?.isNative?.(obj)) {
    return ' '
  }

  // 数字类型直接返回
  if (_?.isNumber?.(obj)) {
    return obj;
  }

  // 安全整数直接返回
  if (_?.isSafeInteger?.(obj)) {
    return obj;
  }

  // 字符串类型直接返回
  if (_?.isString?.(obj)) {
    return obj;
  }

  // Symbol 类型返回空格
  if (_?.isSymbol?.(obj)) {
    return ' '
  }

  // 其他未知类型返回空格
  return ' '
}

/**
 * 生成 IN 查询的 WHERE 条件（使用 OR 连接）
 * @param columnName - 字段名，默认为 'id'
 * @param columnIds - 字段值数组
 * @returns WHERE 条件字符串，使用 OR 连接多个等值条件
 * 
 * 示例：
 * 输入: columnName = 'status', columnIds = ['active', 'pending']
 * 输出: " AND (  t.status = 'active'  OR t.status = 'pending'  ) "
 */
export function whereOr(columnName = 'id', columnIds: any[] = []): string {
  let whereSql = ' ';

  // 如果没有值数组或数组为空，返回空格
  if (!columnIds || !columnIds?.length) return whereSql;

  // 将字段名转换为蛇形命名法
  columnName = _?.snakeCase?.(columnName);

  // 开始构建 OR 条件
  whereSql += ' AND ( ';

  // 遍历每个值，生成等值条件
  columnIds?.forEach?.((item, index) => {
    if (index < 1) {
      // 第一个条件不加 OR
      whereSql += ' t.' + columnName + ` = '${item}' `;
    } else {
      // 后续条件加 OR
      whereSql += ' OR t.' + columnName + ` = '${item}' `;
    }
  });

  whereSql += ' ) ';

  return whereSql;
}

/**
 * 根据单个规则生成 WHERE 条件字符串
 * @param rule - 规则对象，包含 field（字段）、operator（操作符）、value（值）等
 * @returns 单个规则的 WHERE 条件字符串
 * 
 * 支持的操作符：
 * - contains: LIKE '%value%'
 * - beginsWith: LIKE 'value%'
 * - endsWith: LIKE '%value'
 * - null: IS NULL OR = ''
 * - notNull: IS NOT NULL AND != ''
 * - >, <, <=, >=: 数值比较
 * - 其他: 等值比较
 */
function ruleStr(rule: any = {}): string {
  let whereSql = ' ';

  const { field, operator, valueSource, value } = rule;

  // 包含操作符：模糊匹配
  if (operator === 'contains') {

    whereSql += ` ( ${field} LIKE '%${value}%' ) `;

    return whereSql;

  } else if (operator === 'beginsWith') {
    // 以...开始：前缀匹配

    whereSql += ` ( ${field} LIKE '${value}%' ) `;

    return whereSql;

  } else if (operator === 'endsWith') {
    // 以...结束：后缀匹配

    whereSql += ` ( ${field} LIKE '%${value}' ) `;

    return whereSql;

  } else if (operator === 'null') {
    // 空值判断：NULL 或空字符串

    whereSql += ` ( ${field} IS NULL OR ${field} = '' ) `;

    return whereSql;

  } else if (operator === 'notNull') {
    // 非空值判断：非 NULL 且非空字符串

    whereSql += ` ( ${field} IS NOT NULL AND ${field} != '' ) `;

    return whereSql;

  } else if (operator === '>' || operator === '<' || operator === '<=' || operator === '>=') {
    // 数值比较操作符：直接使用操作符

    whereSql += ` ( ${field} ${operator} ${value} ) `;

    return whereSql;

  }

  // 记录 valueSource 用于调试
  console?.log?.(valueSource);

  // 默认情况：等值比较，字符串加引号
  whereSql += ` ( ${field} ${operator} '${value}' ) `;

  return whereSql;
}

/**
 * 组合多个规则生成复合 WHERE 条件
 * @param rule - 规则对象，包含 combinator（组合方式）和 rules（规则数组）
 * @returns 组合后的 WHERE 条件字符串
 * 
 * 组合方式：
 * - 'or': 使用 OR 连接所有规则
 * - 其他: 使用 AND 连接所有规则
 */
export function rulesCombinator(rule: any = {}): string {
  let whereSql = ' ';

  let { combinator, rules } = rule;

  // 如果没有规则，返回空格
  if (!rule) { return whereSql; }

  // 根据组合方式设置连接符
  if (combinator === 'or') {

    combinator = ' OR ';

  } else {

    combinator = ' AND ';

  }

  // 开始构建复合条件
  whereSql += ' ( ';

  // 遍历每个规则
  for (let i = 0; i < rules?.length; i++) {

    // 第一个规则不加连接符
    if (i > 0) {

      whereSql += combinator;

    }

    const r = rules?.[i];

    // 调用 ruleStr 生成单个规则的条件
    whereSql += ruleStr?.(r);

  }

  whereSql += ' ) ';

  return whereSql;

}

/**
 * 解析查询字符串并生成 WHERE 条件
 * @param query - URL 编码的查询字符串
 * @returns WHERE 条件字符串
 * 
 * 处理流程：
 * 1. URL 解码
 * 2. JSON 解析
 * 3. 调用 rulesCombinator 生成条件
 */
export function query(query = ''): string {
  let whereSql = ' ';

  // 如果没有查询字符串，返回空格
  if (!query) return whereSql;

  // URL 解码并 JSON 解析
  const queryObj = JSON?.parse?.(decodeURIComponent?.(query));

  // 生成 WHERE 条件并添加 AND 前缀
  whereSql += ' AND ' + rulesCombinator?.(queryObj);

  return whereSql;

}

/**
 * 解析过滤器字符串并生成 WHERE 条件
 * @param filtersStr - URL 编码的过滤器字符串
 * @returns WHERE 条件字符串
 * 
 * 过滤器格式：
 * - 字符串值：生成 LIKE 模糊查询
 * - 数组值：生成 IN 查询（使用 whereOr 函数）
 */
export function filters(filtersStr = ''): string {

  let whereSql = ' ';

  // 如果没有过滤器字符串，返回空格
  if (!filtersStr) return whereSql;

  // URL 解码并 JSON 解析
  const filters: any[] = JSON?.parse?.(decodeURIComponent?.(filtersStr));

  // 如果没有过滤器或过滤器为空，返回空格
  if (!filters || filters?.length < 1) return whereSql;

  // 遍历过滤器的每个键值对
  for (const key in filters) {

    const val = filters?.[key];

    // 如果值是字符串，生成 LIKE 模糊查询
    if (typeof val === 'string') {

      whereSql += ` AND t.${_?.snakeCase?.(key)} LIKE '%${val}%' `;

      // 继续处理下一个键值对
      continue;

    }

    // 如果值是数组，生成 IN 查询
    if (Array?.isArray?.(val)) {

      whereSql += whereOr?.(key, val);

    }

  }

  return whereSql;

}

/**
 * 将过滤器转换为 OR 连接的 WHERE 条件
 * @param filtersStr - URL 编码的过滤器字符串
 * @returns WHERE 条件字符串，使用 OR 连接不同字段的 IN 条件
 * 
 * 与 filters 函数的区别：
 * - filters: 使用 AND 连接所有条件
 * - whereOrFilters: 使用 OR 连接不同字段的条件
 */
export function whereOrFilters(filtersStr = ''): string {

  let whereSql = ' ';

  // 如果没有过滤器字符串，返回空格
  if (!filtersStr) return whereSql;

  // URL 解码并 JSON 解析
  const filters: any[] = JSON?.parse?.(decodeURIComponent?.(filtersStr));

  // 如果没有过滤器，返回空格
  if (!filters) return whereSql;

  // 使用 arrayUtils.getKeyObj 将数组转换为对象
  const filterObj: any = arrayUtils?.getKeyObj?.(filters);

  // 获取过滤器的所有键
  const filterKeys: string[] = _?.keys?.(filterObj);

  // 为每个键生成 IN 查询条件，并用 OR 连接
  for (const key of filterKeys) {

    whereSql += whereOr?.(key, filterObj?.[key]);

  }

  return whereSql;
}

/**
 * 生成时间区间交集的 WHERE 条件
 * @param start - 开始时间字符串
 * @param end - 结束时间字符串
 * @param startColumnName - 开始时间字段名
 * @param endColumnName - 结束时间字段名
 * @returns WHERE 条件字符串，用于判断两个时间区间是否有交集
 * 
 * 交集判断逻辑：
 * 第一个区间的开始时间 < 第二个区间的结束时间 AND
 * 第一个区间的结束时间 > 第二个区间的开始时间
 */
export function intersectionTime(start: string = '', end: string = '', startColumnName: string = '', endColumnName: string = ''): string {

  // 判断两个时间区间是否有交集
  const sql = ` AND ( ${startColumnName} < STR_TO_DATE('${end}', '%Y-%m-%d %H:%i:%s') AND ${endColumnName} > STR_TO_DATE('${start}', '%Y-%m-%d %H:%i:%s') ) `

  return sql

}

/**
 * 生成多字段模糊查询的 WHERE 条件
 * @param arr - 字段值数组，每个元素包含 label（字段名）和 value（搜索值）
 * @param formName - 表别名，默认为 't'
 * @returns WHERE 条件字符串，使用 AND 连接多个 LIKE 条件
 * 
 * 示例：
 * 输入: arr = [{ label: 'name', value: 'john' }, { label: 'email', value: 'gmail' }]
 * 输出: " AND ( 1>0  AND t.name LIKE '%john%'  AND t.email LIKE '%gmail%'  ) "
 */
export function mulColumnLike(arr: any[], formName = 't'): string {

  // 如果没有数组或数组为空，返回空格
  if (!arr || arr?.length < 1) {

    return ' '

  }

  // 开始构建多字段 LIKE 条件
  let sql = ' AND ( 1>0 '

  // 遍历每个字段值对
  for (const item of arr) {

    // 将字段名转换为蛇形命名法
    const label = _?.snakeCase?.(item?.label)

    // 生成 LIKE 条件
    sql += ` AND ${formName}.${label} LIKE '%${item?.value}%' `

  }

  sql += ' ) '

  return sql

}
